use crate::ref_to_mut;
use alloc::{
    collections::BTreeMap,
    string::{String, ToString},
    sync::Arc,
    vec::Vec,
};
use spin::RwLock;
use syscall::scheme::{FileInfo, KernelScheme, SchemeRef, SchemeTy};

use super::{ROOT_INODE, mount_to};

pub struct DevScheme {
    nodes: BTreeMap<String, SchemeRef>,
    path: String,
}

impl DevScheme {
    pub fn new() -> SchemeRef {
        let scheme = Arc::new(RwLock::new(Self {
            nodes: BTreeMap::new(),
            path: String::new(),
        }));
        scheme.write().nodes.insert(".".into(), scheme.clone());
        scheme
    }
}

impl KernelScheme for DevScheme {
    fn when_mounted(&mut self, path: String, father: Option<SchemeRef>) {
        self.path.clear();
        self.path.push_str(path.as_str());
        if let Some(father) = father {
            self.nodes.insert("..".into(), father.clone());
        }
    }

    fn when_umounted(&mut self) {
        for (name, node) in self.nodes.iter() {
            if name != "." && name != ".." {
                node.write().when_umounted();
            }
        }
    }

    fn mount(&mut self, node: SchemeRef, name: String) {
        ref_to_mut(self).nodes.insert(name, node);
    }

    fn get_path(&self) -> String {
        self.path.clone()
    }

    fn open(&self, name: String) -> Option<SchemeRef> {
        self.nodes.get(&name).cloned()
    }

    fn scheme_type(&self) -> SchemeTy {
        SchemeTy::Dir
    }

    fn list(&self) -> alloc::vec::Vec<FileInfo> {
        let mut vec = Vec::new();
        for (name, scheme) in self.nodes.iter() {
            vec.push(FileInfo::new(name.clone(), scheme.read().scheme_type()));
        }
        vec
    }
}

pub fn init() {
    let dev_scheme = DevScheme::new();
    mount_to(
        dev_scheme.clone(),
        ROOT_INODE.lock().clone(),
        "dev".to_string(),
    );
}
